﻿using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using ThePresenter.Net.PresenterCom.Uids;
using ThePresenter.Polls.Data;
using System.IO;
using System.Collections.Generic;


namespace ThePresenter.Polls
{
	[Serializable]
	public class Poll : INotifyPropertyChanged, ISerializable
	{
		#region Fields
		internal static readonly BinaryFormatter Formatter = new BinaryFormatter();

		readonly Uid _GUID;

		readonly DateTime _TimeStamp;

		QuestionData _QuestionData;

		OptionsData _OptionsData;

		/// <summary>
		/// Contains clients answer at clients side
		/// </summary>
		AnswerData _AnswerData;

		string _Title;

		ClientPollState _State;

		/// <summary>
		/// Contains answers from clients at server side
		/// </summary>
		PollAnswersSet _Answers;
		#endregion


		#region Properties
		/// <summary>
		/// Gets or sets the title.
		/// </summary>
		/// <value>The title.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		public string Title
		{
			get
			{
				return _Title;
			}
			set
			{
				if (_Title != value)
				{
					_Title = value;
					OnPropertyChanged("Title");
				}
			}
		}

		/// <summary>
		/// Gets the GUID.
		/// </summary>
		/// <value>The GUID.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		public Uid GUID
		{
			get
			{
				return _GUID;
			}
		}

		/// <summary>
		/// Gets the time stamp.
		/// </summary>
		/// <value>The time stamp.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public DateTime TimeStamp
		{
			get
			{
				return _TimeStamp;
			}
		}

		/// <summary>
		/// Gets or sets the data of the question.
		/// </summary>
		/// <value>The data of the question.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		public QuestionData QuestionData
		{
			get
			{
				return _QuestionData;
			}
			set
			{
				if (_QuestionData != value)
				{
					_QuestionData = value;
					OnPropertyChanged("QuestionData");
				}
			}
		}

		/// <summary>
		/// Gets or sets the data of the answer.
		/// </summary>
		/// <value>The data of the answer.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		public OptionsData OptionsData
		{
			get
			{
				return _OptionsData;
			}
			set
			{
				if (_OptionsData != value)
				{
					_OptionsData = value;

					this.Answers.Options = value;

					switch (value.OptionsType)
					{
						case OptionsType.Text:
							this.AnswerData = new TextAnswerData();
							break;
						case OptionsType.Canvas:
							this.AnswerData = new CanvasAnswerData();
							break;
						case OptionsType.YesNo:
							this.AnswerData = new YesNoAnswerData();
							break;
						case OptionsType.PickOne:
							this.AnswerData = new PickOneAnswerData();
							break;
						case OptionsType.PickSome:
							List<bool> AnswerOptions = new List<bool>(((SelectOptionsData)value).Options.Count);
							for (int i = 0; i < AnswerOptions.Capacity; i++)
								AnswerOptions.Add(false);

							this.AnswerData = new PickSomeAnswerData()
							{
								Options = AnswerOptions
							};
							break;
						default:
							break;
					}

					OnPropertyChanged("OptionsData");
				}
			}
		}


		/// <summary>
		/// Gets or sets the answer data.
		/// Used by client to hold client's answer.
		/// </summary>
		/// <value>The answer data.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		public AnswerData AnswerData
		{
			get
			{
				return _AnswerData;
			}
			set
			{
				if (_AnswerData != value)
				{
					_AnswerData = value;
					OnPropertyChanged("AnswerData");
				}
			}
		
		}

		/// <summary>
		/// Gets the question of poll.
		/// </summary>
		/// <value>The question.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public String Question
		{
			get {
				if (this.QuestionData != null)
					return this.QuestionData.Question;
				else
					return null;				
			}
		}

		/// <summary>
		/// Gets the answers count.
		/// </summary>
		/// <value>The answers count.</value>
		public int AnswersCount
		{
			get { return this._Answers.Count; } 
		}

		/// <summary>
		/// Gets the answers.
		/// </summary>
		/// <value>The answers.</value>
		public PollAnswersSet Answers
		{
			get { return this._Answers; }
		}

		/// <summary>
		/// Gets the filtered answers.
		/// </summary>
		/// <value>The filtered answers.</value>
		public PollAnswersSet FilteredAnswers
		{
			get
			{
				if (this.OptionsData.OptionsType == OptionsType.Canvas)
					return this._Answers;
				else
					return null;
			}
		}

		/// <summary>
		/// Gets or sets the poll state.
		/// </summary>
		/// <value>The state of poll.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public ClientPollState State
		{
			get{
				if(_State == ClientPollState.Unknown)
					_State = ClientPollState.Received;
				return _State;
			}
			set
			{
				if (_State != value)
				{
					_State = value;
					OnPropertyChanged("State");
				}
			}
		}
		#endregion


		#region Constructors
		/// <summary>
		/// Initializes a new instance of the <see cref="Poll"/> class.
		/// </summary>
		public Poll()
		{
			_GUID = new Uid();
			_TimeStamp = DateTime.Now;
			_Answers = new PollAnswersSet();
		}
		#endregion


		#region Methods
		public void HandleAnswer(AnswerData A)
		{
			this._Answers.ProcessAnswer(A);
		}

		/// <summary>
		/// Serializes this instance.
		/// </summary>
		/// <returns>Bytes representing this instance</returns>
		public byte[] Serialize()
		{
			byte[] bytes = null;

			using (var ms = new MemoryStream())
			{
				Poll.Formatter.Serialize(ms, this);
				bytes = ms.ToArray();
			}

			return bytes;
		}

		public override string ToString()
		{
			return String.Format("{0}: {1}?{2}", this._Title, this._QuestionData.QuestionType, this._OptionsData.OptionsType);
		}
		#endregion


		#region INotifyPropertyChanged Members

		/// <summary>
		/// Occurs when a property value changes.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Called when property changed.
		/// </summary>
		/// <param name="propertyName">Name of the property.</param>
		protected void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}

		#endregion


		#region ISerializable Members
		/// <summary>
		/// Initializes a new instance of the <see cref="Poll"/> class.
		/// </summary>
		/// <param name="info">The info.</param>
		/// <param name="context">The context.</param>
		public Poll(SerializationInfo info, StreamingContext context)
		{
			_GUID = (Uid)info.GetValue("Guid", typeof(Uid));
			_TimeStamp = (DateTime)info.GetValue("TimeStamp", typeof(DateTime));
			_Title = (String)info.GetValue("Title", typeof(String));
			_QuestionData = (QuestionData)info.GetValue("QuestionData", typeof(QuestionData));
			_OptionsData = (OptionsData)info.GetValue("OptionsData", typeof(OptionsData));
			_AnswerData = (AnswerData)info.GetValue("AnswerData", typeof(AnswerData));
			_Answers = new PollAnswersSet();
		}

		/// <summary>
		/// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
		/// </summary>
		/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
		/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
		/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("Guid", _GUID, typeof(Uid));
			info.AddValue("TimeStamp", _TimeStamp, typeof(DateTime));
			info.AddValue("Title", _Title, typeof(String));
			info.AddValue("QuestionData", _QuestionData, typeof(QuestionData));
			info.AddValue("OptionsData", _OptionsData, typeof(OptionsData));
			info.AddValue("AnswerData", _AnswerData, typeof(AnswerData)); // TODO: mozno sa nemusia serializovat, klient ich nepotrebuje zo servera
		}
		#endregion
	}
}
